﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Threading;
using System.Web;
using System.Net;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Net.Json;

namespace Rookey.Frame.IMClient
{

	public class Utility
	{
		static DateTime BaseDateTime = new DateTime(1970, 1, 1, 0, 0, 0);

		public static String RenderJson(object obj)
		{
			StringBuilder builder = new StringBuilder();
			RenderJson(builder, obj);
			return builder.ToString();
		}

		public static void RenderJson(StringBuilder builder, object obj)
		{
			if (obj == null)
			{
				builder.Append("null");
			}
			else if (obj is Exception)
			{
				builder.AppendFormat("{{\"__DataType\":\"Exception\",\"__Value\":{{\"Name\":\"{0}\",\"Message\":\"{1}\"}}}}", obj.GetType().Name, Utility.TransferCharJavascript((obj as Exception).Message));
			}
			else if (obj.GetType() == typeof(DateTime))
			{
				DateTime val = (DateTime)obj;
				RenderHashJson(
					builder,
					"__DataType", "Date",
					"__Value", (val - BaseDateTime).TotalMilliseconds
				);
			}
			else if (obj is IDictionary)
			{
				int count = 0;
				builder.Append("{");
				foreach (DictionaryEntry ent in (obj as IDictionary))
				{
					if (count > 0) builder.Append(",");
					builder.AppendFormat("\"{0}\":", Utility.TransferCharJavascript(ent.Key.ToString()));
					RenderJson(builder, ent.Value);
					count++;
				}
				builder.Append("}");
			}
			else if (obj is IList)
			{
				IList list = obj as IList;
				builder.Append("[");
				for (int i = 0; i < list.Count; i++)
				{
					if (i > 0) builder.Append(",");
					RenderJson(builder, list[i]);

				}
				builder.Append("]");
			}
			else if (obj is ICollection)
			{
				ICollection list = obj as ICollection;
				builder.Append("[");
				int count = 0;
				foreach (object val in list)
				{
					if (count > 0) builder.Append(",");
					RenderJson(builder, val);
					count++;
				}
				builder.Append("]");
			}
			else if (obj is DataRow)
			{
				DataRow row = obj as DataRow;
				builder.Append("{");

				int count = 0;
				foreach (DataColumn column in row.Table.Columns)
				{
					if (count > 0) builder.Append(",");
					builder.AppendFormat("\"{0}\":", column.ColumnName);
					RenderJson(builder, row[column.ColumnName]);
					count++;
				}

				builder.Append("}");
			}
			else if (obj is System.Drawing.Rectangle)
			{
				System.Drawing.Rectangle rect = (System.Drawing.Rectangle)obj;

				RenderHashJson(
					builder,
					"Left", rect.Left,
					"Top", rect.Top,
					"Width", rect.Width,
					"Height", rect.Height
				);
			}
			else if (obj is UInt16 || obj is UInt32 || obj is UInt64 || obj is Int16 || obj is Int32 || obj is Int64 || obj is Double || obj is Decimal || obj is long)
			{
				builder.Append(obj.ToString());
			}
			else if (obj is bool)
			{
				builder.Append((bool)obj ? "true" : "false");
			}
			else
			{
				builder.Append("\"");
				builder.Append(TransferCharJavascript(obj.ToString()));
				builder.Append("\"");
			}
		}

		public static String RenderHashJson(params object[] ps)
		{
			StringBuilder builder = new StringBuilder();
			builder.Append("{");
			for (int i = 0; i < ps.Length; i += 2)
			{
				if (i > 0) builder.Append(",");
				builder.AppendFormat("\"{0}\":", ps[i].ToString());
				RenderJson(builder, ps[i + 1]);
			}
			builder.Append("}");
			return builder.ToString();
		}

		public static void RenderHashJson(StringBuilder builder, params object[] ps)
		{
			builder.Append("{");
			for (int i = 0; i < ps.Length; i += 2)
			{
				if (i > 0) builder.Append(",");
				builder.AppendFormat("\"{0}\":", ps[i].ToString());
				RenderJson(builder, ps[i + 1]);
			}
			builder.Append("}");
		}

		public static string TransferCharJavascript(string s)
		{
			StringBuilder ret = new StringBuilder();
			foreach (char c in s)
			{
				switch (c)
				{
				case '\r':
				case '\t':
				case '\n':
				case '\f':
				case '\v':
				case '\"':
				case '\\':
				case '\'':
				case '<':
				case '>':
				case '\0':
					ret.AppendFormat("\\u{0:X4}", (int)c);
					break;
				default:
					ret.Append(c);
					break;
				}
			}
			return ret.ToString();
		}

		public static Object ParseJson(string jsonText)
		{
			if (!string.IsNullOrEmpty(jsonText))
			{
				JsonTextParser parser = new JsonTextParser();
				JsonObject obj = parser.Parse(jsonText);
				return ParseJson(obj);
			}
			else
			{
				return null;
			}
		}

		private static Object ParseJson(JsonObject jsonObject)
		{
			Type type = jsonObject.GetType();
			if (type == typeof(JsonObjectCollection))
			{
				Hashtable val = new Hashtable();
				foreach (JsonObject subObj in (jsonObject as JsonObjectCollection))
				{
					val.Add(subObj.Name, ParseJson(subObj));
				}
				if (val.ContainsKey("__DataType"))
				{
					if (val["__DataType"] as string == "Date")
					{
						return BaseDateTime.AddMilliseconds((Double)val["__Value"]);
					}
					else if (val["__DataType"] as string == "Exception")
					{
						return new Exception((val["__Value"] as Hashtable)["Message"] as string);
					}
					else
					{
						return val;
					}
				}
				else
				{
					return val;
				}
			}
			else if (type == typeof(JsonArrayCollection))
			{
				List<object> val = new List<object>();
				foreach (JsonObject subObj in (jsonObject as JsonArrayCollection))
				{
					val.Add(ParseJson(subObj));
				}
				return val.ToArray();
			}
			else if (type == typeof(JsonBooleanValue))
			{
				return jsonObject.GetValue();
			}
			else if (type == typeof(JsonStringValue))
			{
				return jsonObject.GetValue();
			}
			else if (type == typeof(JsonNumericValue))
			{
				return jsonObject.GetValue();
			}
			else
				return null;
		}

		static public HtmlElement Find(HtmlElement elem, string classPath)
		{
			string[] cpn = classPath.Split(new char[] { ',' });
			return Find(elem, cpn, 0);
		}

		static public HtmlElement Find(HtmlElement elem, string[] cpn, int first)
		{
			for (int i = 0; i < elem.Children.Count; i++)
			{
				object dom = elem.Children[i].DomElement;

				string className = (string)dom.GetType().InvokeMember("className", BindingFlags.GetProperty, null, dom, new object[0]);

				if (String.Compare(className, cpn[first], true) == 0)
				{
					return first == cpn.Length - 1 ? elem.Children[i] : Find(elem.Children[i], cpn, first + 1);
				}
			}

			return null;
		}

		static public object GetProperty(object dom, string name)
		{
			return dom.GetType().InvokeMember(name, BindingFlags.GetProperty, null, dom, new object[0]);
		}

		static public object SetProperty(object dom, string name, object value)
		{
			return dom.GetType().InvokeMember(name, BindingFlags.SetProperty, null, dom, new object[] { value });
		}

		static public object InvokeMethod(object obj, string name, params object[] args)
		{
			return obj.GetType().InvokeMember(name, BindingFlags.InvokeMethod, null, obj, args);
		}

		static public object CallFunc(object func, params object[] args)
		{
			List<object> ps = new List<object>();
			ps.Add(func);
			ps.AddRange(args);

			return func.GetType().InvokeMember("call", BindingFlags.InvokeMethod, null, func, ps.ToArray());
		}
	}

	public static class HtmlUtil
	{
		static Hashtable AllowHtmlTag = new Hashtable();
		static Regex HtmlTagRegex = new Regex(@"<(\/|)([^ \f\n\r\t\v\<\>]+)(\s[^\<\>]*|)>");
		static Regex ImpermitWordRegex = new Regex("([^a-zA-Z1-9_])(on|expression|javascript)", RegexOptions.IgnoreCase);

		static HtmlUtil()
		{
			AllowHtmlTag.Add("I", "I");
			AllowHtmlTag.Add("B", "B");
			AllowHtmlTag.Add("U", "U");
			AllowHtmlTag.Add("P", "P");
			AllowHtmlTag.Add("TH", "TH");
			AllowHtmlTag.Add("TD", "TD");
			AllowHtmlTag.Add("TR", "TR");
			AllowHtmlTag.Add("OL", "OL");
			AllowHtmlTag.Add("UL", "UL");
			AllowHtmlTag.Add("LI", "LI");
			AllowHtmlTag.Add("BR", "BR");
			AllowHtmlTag.Add("H1", "H1");
			AllowHtmlTag.Add("H2", "H2");
			AllowHtmlTag.Add("H3", "H3");
			AllowHtmlTag.Add("H4", "H4");
			AllowHtmlTag.Add("H5", "H5");
			AllowHtmlTag.Add("H6", "H6");
			AllowHtmlTag.Add("H7", "H7");
			AllowHtmlTag.Add("EM", "EM");
			AllowHtmlTag.Add("PRE", "PRE");
			AllowHtmlTag.Add("DIV", "DIV");
			AllowHtmlTag.Add("IMG", "IMG");
			AllowHtmlTag.Add("CITE", "CITE");
			AllowHtmlTag.Add("SPAN", "SPAN");
			AllowHtmlTag.Add("FONT", "FONT");
			AllowHtmlTag.Add("CODE", "CODE");
			AllowHtmlTag.Add("TABLE", "TABLE");
			AllowHtmlTag.Add("TBODY", "TBODY");
			AllowHtmlTag.Add("SMALL", "SMALL");
			AllowHtmlTag.Add("THEAD", "THEAD");
			AllowHtmlTag.Add("CENTER", "CENTER");
			AllowHtmlTag.Add("STRONG", "STRONG");
			AllowHtmlTag.Add("BLOCKQUOTE", "BLOCKQUOTE");
		}

		static public String ReplaceHtml(String text)
		{
			return HtmlTagRegex.Replace(text, ReplaceHtmlTag);
		}

		static public string ReplaceHtmlTag(Match match)
		{
			if (match.Groups[1].Value == "/")
			{
				if (AllowHtmlTag.ContainsKey(match.Groups[2].Value.ToUpper()))
				{
					return match.Value;
				}
				else
				{
					return "&lt;/" + match.Groups[2].Value + "&gt;";
				}
			}
			else
			{
				if (AllowHtmlTag.ContainsKey(match.Groups[2].Value.ToUpper()))
				{
					return ImpermitWordRegex.Replace(match.Value, ReplaceImpermitWord);
				}
				else
				{
					return "&lt;" + match.Groups[2].Value + "&gt;";
				}
			}
		}

		static public string ReplaceImpermitWord(Match match)
		{
			return match.Groups[1].Value + "_" + match.Groups[2].Value;
		}
	}

	public interface IRenderJson
	{
		void RenderJson(StringBuilder builder);
	}

}
